{# Base layout every page extends.
Blocks: title, favicon, head_extra, navbar, page_body, content, footer,
scripts. Override ``content`` for an ordinary page; reach for
``page_body`` only when a page needs to replace the whole main wrapper.
#}
{% block title %}{{ project_name }}{% endblock %}
{# Favicon: SVG only. Modern browsers handle it natively (auto-scales,
respects prefers-color-scheme inside the SVG, and the bytes are
smaller than maintaining a multi-resolution ICO). Older browsers and
bots that auto-request /favicon.ico will 404 — harmless. #}
{% block favicon %}{% endblock %}
{# Tailwind + DaisyUI, precompiled — no runtime JIT, no CDN. Built into
static/dist by the asset pipeline; static() returns the fingerprinted
URL once a build exists and the raw source path before then. #}
{# htmx. History snapshots are disabled deliberately: htmx's default
back-button behavior saves the live DOM to localStorage and re-injects
it on history navigation. This DOM is full of Alpine-expanded
templates (x-if/x-for clones, x-teleport copies) and script tags —
restoring a snapshot re-runs scripts ("identifier already declared")
and makes Alpine re-expand already-expanded templates, duplicating the
whole page once per back/forward press. historyCacheSize 0 makes every
restore a cache miss; refreshOnHistoryMiss turns a miss into a clean
full-page load instead of an AJAX restore. #}
{# Alpine.js — the collapse plugin MUST load before core so its directive
is registered when Alpine inits. Both deferred, plugin first. #}
{# Custom styles #}
{% block head_extra %}{% endblock %}
{% block navbar %}